﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Data.SugarClient.Soap.V1;
using System.Data.SugarClient.Soap.V2;

namespace System.Data.SugarClient
{

	internal class SugarSoapContext
	{

		internal SugarSoapContext(
			string url)
		{
			this.Url = url;
		}

		#region Fields

		private readonly SugarSoapV1.sugarsoap _soapV1 = new SugarSoapV1.sugarsoap();
		private readonly SugarSoapV2.sugarsoap _soapV2 = new SugarSoapV2.sugarsoap();
		private string _url;
		private string _session = string.Empty;

		#endregion 

		#region Properties

		protected internal string Session
		{
			get { return this._session; }
		}

		internal SugarSoapV1.sugarsoap SoapV1
		{
			get { return _soapV1; }
		}

		internal SugarSoapV2.sugarsoap SoapV2
		{
			get { return _soapV2; }
		}

		internal int Timeout
		{
			get { return this._soapV1.Timeout; }
			set { this._soapV1.Timeout = value; }
		}

		internal string Url
		{
			get { return this._url; }
			set
			{
				if (this.Url == value) return;
				this._url = value;
				this.OnUrlChanged(new EventArgs());
			}
		}

		#endregion

		#region Methods

		protected void OnUrlChanged(EventArgs e)
		{
			this._soapV1.Url = this.GetSoapUrl(1);
			this._soapV2.Url = this.GetSoapUrl(2);
		}

		internal void Login(string username, string password)
		{
			this.Login(username, password, string.Empty);
		}

		internal void Login(string username, string password, string encryptionkey)
		{
			V2LoginActionResult result = this.ExecuteAction<V2LoginActionResult>(SugarSoapActionType.Login, 2, username, password, encryptionkey);
			if (result.Success) this._session = result.SessionId;
			else throw new SugarException(string.Format("Can not login to Sugar instance. {0}", "a")); // result.RawResult.error.description));
		}

		internal void Logout()
		{
			this._soapV1.logout(this._session);
		}

		internal string GetServerVersion()
		{
			return this._soapV1.get_server_version();		
		}

		internal TResult ExecuteAction<TResult>(
			SugarSoapActionType type,
			params object[] args) where TResult : class, ISugarSoapActionResult
		{
			return this.ExecuteAction<TResult>(type, this.DetectVersion(), args);
		}

		internal TResult ExecuteAction<TResult>(
			SugarSoapActionType type,
			int apiversion,
			params object[] args) where TResult : class, ISugarSoapActionResult
		{
			return this.ExecuteAction(type, apiversion, args) as TResult;
		}

		internal ISugarSoapActionResult ExecuteAction(
			SugarSoapActionType type,
			params object[] args)
		{
			return this.CreateAction(type, this.DetectVersion(), args).Execute();
		}

		internal ISugarSoapActionResult ExecuteAction(
			SugarSoapActionType type,
			int apiversion,
			params object[] args)
		{
			return this.CreateAction(type, apiversion, args).Execute();
		}


		internal ISugarSoapAction CreateAction(
			SugarSoapActionType type,
			params object[] args)
		{
			return CreateAction(type, DetectVersion(), args);
		}

		internal ISugarSoapAction CreateAction(
			SugarSoapActionType type,
			int apiversion,
			params object[] args)
		{
			switch (type)
			{
				case SugarSoapActionType.GetAvailableModules: return CreateV1GetAvailableModulesAction();
				case SugarSoapActionType.GetEntry: return CreateGetEntryAction(apiversion, args);
				case SugarSoapActionType.GetEntryList: return CreateGetEntryListAction(apiversion, args);
				case SugarSoapActionType.GetModuleFields: return CreateGetModuleFieldsAction(apiversion, args);
				case SugarSoapActionType.GetUserId: return CreateGetUserIdAction(apiversion, args);
				case SugarSoapActionType.Login: return CreateLoginAction(apiversion, args);
				case SugarSoapActionType.SetEntry: return CreateSetEntryAction(apiversion, args);
			}
			throw new NotImplementedException();
		}

		#region CreateXXXAction

		

		internal ISugarSoapAction CreateGetEntryAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1GetEntryAction(args);
			}
			throw new NotImplementedException();
		}

		internal ISugarSoapAction CreateGetEntryListAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1GetEntryListAction(args);
			}
			throw new NotImplementedException();
		}

		internal ISugarSoapAction CreateSetEntryAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1SetEntryAction(args);
			}
			throw new NotImplementedException();
		}

		internal ISugarSoapAction CreateGetModuleFieldsAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1GetModuleFieldsAction(args);
			}
			throw new NotImplementedException();
		}

		internal ISugarSoapAction CreateGetUserIdAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1GetUserIdAction(args);
			}
			throw new NotImplementedException();
		}

		internal ISugarSoapAction CreateLoginAction(
			int apiversion,
			params object[] args)
		{
			switch (apiversion)
			{
				case 1: return CreateV1LoginAction(args);
				case 2: return CreateV2LoginAction(args);
			}
			throw new NotImplementedException();
		}

		#endregion

		internal ISugarSoapAction CreateV1GetAvailableModulesAction()
		{
			return new V1GetAvailableModulesAction(this.SoapV1, this.GetValidSessionIdOrThrow());
		}

		internal ISugarSoapAction CreateV1GetEntryAction(
			params object[] args)
		{
			return
				new V1GetEntryAction(
					this.SoapV1,
					this.GetValidSessionIdOrThrow(),
					args[0] as string,		// module
					args[1] as string,		// id
					args[2] as string[]		// fields
				);
		}

		internal ISugarSoapAction CreateV1GetEntryListAction(
			params object[] args)
		{
			return
				new V1GetEntryListAction(
					this.SoapV1,
					this.GetValidSessionIdOrThrow(),
					args[0] as string,		// module
					args[1] as string,		// criteria
					args[2] as string,		// order,
					(int)args[3],					// offset,
					args[4] as string[],	// fields,
					(int)args[5],					// limit,
					(bool)args[6]					// delete
				);
		}

		internal ISugarSoapAction CreateV1GetModuleFieldsAction(
			params object[] args)
		{
			return
				new V1GetModuleFieldsAction(
					this.SoapV1,
					this.GetValidSessionIdOrThrow(), 
					args[0] as string
				);
		}

		internal ISugarSoapAction CreateV1GetUserIdAction(
			params object[] args)
		{
			return new V1GetUserIdAction(this.SoapV1, this.GetValidSessionIdOrThrow());
		}

		internal ISugarSoapAction CreateV1LoginAction(
			params object[] args)
		{
			return new V1LoginAction(this.SoapV1, args[0] as string, args[1] as string, args[2] as string);
		}

		internal ISugarSoapAction CreateV2LoginAction(
			params object[] args)
		{
			return new V2LoginAction(this.SoapV2, args[0] as string, args[1] as string, args[2] as string);
		}

		internal ISugarSoapAction CreateV1SetEntryAction(
			params object[] args)
		{
			return 
				new V1SetEntryAction(
					this.SoapV1,
					this.GetValidSessionIdOrThrow(),
					args[0] as string,
					args[1] as IDictionary<string,object>
				);
		}

		protected int DetectVersion()
		{
			return 1;
		}

		protected string GetValidSessionIdOrThrow()
		{
			if (string.IsNullOrEmpty(this.Session)) throw new SugarException("Login First!");
			else return this._session;
		}

		protected string GetSoapUrl(int apiversion)
		{
			string format = (this.Url.EndsWith("/")) ? "" : this.Url + "/";
			switch(apiversion)
			{
				case 1: format += "{0}soap.php"; break;
				case 2: format += "{0}service/v2/soap.php"; break;
				default: throw new NotImplementedException();
			}
			return string.Format(format, this.Url);
		}

		#endregion

	}
}